I need to be honest
Can you break me? Or do you need to be incredibly honest to figure out what I'm hiding? Find my password and get the 3 things I'm hiding from you.
This binary has 3 flags that need to be decrypted, after the correct password is entered. The flags are encrypted through XOR, each flag with its own different key. But looking through the program will reveal that the password is actually available in plaintext form in memory.

It starts by allocating 8 bytes of stack space, then writes the title message, intro message, prompt message to the console and requests password input.
The binary relies heavily on Linux syscalls, using sys_write and sys_read for console interaction, sys_write trusts the byte count stored in rdx.
If I patch the binary and increase rdx from the intended message size to 500 bytes, the syscall will continue copying adjacent memory past the legitimate string boundary and print it to the console. This creates a buffer over-read bug, similar concept to the flaw behind Heartbleed, where attacker-controlled length values caused unintended memory disclosure, that exposed sensitive details in memory. -> https://xkcd.com/1354/
To prove this, I set a breakpoint on the sys_write syscall that prints the initial title message. In the debugger, I manually intercepted the RDX register (which held 0x8D = 141 bytes) and changed it to 0x1F4 (500 bytes).

When I stepped over the syscall, the program printed the title message, but because I lied about the length, it just kept reading memory. As you can see in the output below, it spilled beyond the title message, intro, prompt message, the actual password, and one of the decrypted flags straight to the console!

Then a buffer memory address is put in rsi this holds the address that points to the message to be put in the buffer and printed out. Each length defined in rdx register is the exact length of that string.
For the final block the binary reads the input password into bss_start, an uninitialized buffer in the BSS section, with a maximum capacity of 256 bytes (100h). This input is read and the length is calculated and loaded into rax, from this image we can see the supplied input is 18h long = 24 bytes including the trailing newline character (\n). But then that input length is decreased by one to remove the \n (newline character) at the end of the string, and that input string length is re-uploaded into rax for comparison.

rcx, r8, r9 are XORed to clear them. After that, we proceed to the password verify loop which is a loop that checks character by character of the input password Vs the actual password. Starting from index 0. From this we can infer that the required password length is 27 bytes = 1Bh.

rcx holds the counter or index that is used to access characters at different positions in the passwords. Only when the password length reaches 27 bytes will execution proceed to the password checksum verification block.
The first character of the input password buffer (bss_start[0]) is moved into al. Then the address of the actual password is loaded into rbx. I set a breakpoint as shown below to see the password being pointed to by that memory address location. The correct password is therefore revealed as SecurePass_2k26_X64_Reverse.

This is followed by accessing the password byte at the index stored in rcx, which is then moved into bl. The values in al and bl are compared. If they match, the zero flag is set; otherwise program is redirected to the fail stage.

In the fail stage, the failure message to the console, after the fail message buffer address is loaded into rsi. The count for this fail message is 30h = 48 chars. That is output to the console, and then syscall exit is called (syscall 60).
Password Verification - Happy Path
Each character of the input password is moved into rax, with the remaining bits zero-extended. This helps in calculating the checksum by adding r8 + rax = r8 -> input_password_checksum = char[n] + char[n+1], char[n+2]. On the far right of the image, r8 now holds the first character value -> 0x53 for S. On the next round -> it adds the 0x53(S) to 0x65 (e) = 184 in decimal which is 0xB8h and it continues adding the values of the characters until it is done looping through the password to get the checksum.

The counter is increased by 1, and we go back to the password verify loop that checks for the password length.
Once the counter of 27 is reached, we proceed to the verify checksum part which compares the value in r8 which contains our input passwords checksum to the actual passwords checksum stored. From the image we can see it is 9BEh which is 2494 in decimal.

If it matches the flag decryption begins. There are three encrypted flags, each using its own XOR key. Flag one's key is G, flag two's is Z and flag three's is l. They are all printed on a newline as seen with the mov [rps+8+var_8], 0Ah instruction that moves the newline character formed from 0Ah = 10 in decimal in ASCII this represents \n.

However, decrypting the flags is not strictly necessary because they are already exposed as plaintext in memory.

XOR Decrypt Function

The image above shows the XOR decryption loop, which decrypts the flag character by character using the supplied key. Rbx is saved so we can revert back to its values when done with the decryption. R8 is cleared and we move the value inside rax which is the key into rbx. RCX contains the decrypted flags length and R8 is used as a loop counter (index variable) to track the current position during iteration. al which holds the encrypted flag character by character is XORed with the value inside bl which is the key.
Once decryption completes, rbx is restored and the function returns to the caller. The binary then proceeds to exit.
